Prerequisites

For this course, you will need to download R and RStudio. One way to get R is to go to CRAN (Comprehensive R archive Network).

RStudio is the IDE (integrated development environment) we’ll be using. You can download it from http://www.rstudio.com/download.

We will also use the patchwork package to easily arrange many plots into one single figure.

What is the tidyverse?

“The tidyverse is an opinionated collection of R packages designed for data science. All packages share an underlying design philosophy, grammar, and data structures.”

These packages help you import, tidy and understand data, to finally be able to communicate your findings in an easy way.

You can install the core packages from the tidyverse by simply typing install.packages("tidyverse") in the console.

# You only need to install a package once per device
install.packages("tidyverse")

The core tidyverse packages (ggplot2, tibble, tidyr, readr, dplyr, stringr, forcats, purrr) can be loaded then with the library() function.

library(tidyverse)
library(patchwork)

There are many other packages that comply with the tidyverse data structure and practices that have to be installed and loaded independently. Some of those that we’ll be using throughout the course are tsibble, fable, tidymodels.

So, how does each of these packages help us tackle our data science problems?

Data science workflow

We can say that a data science workflow (aimed at forecasting) comprises the following general steps:

1. Import data

Data can be imported into R in many ways. The easiest way is importing files, such as .csv, .txt or MS Excel files. However, you can also import files from other statistical softwares or even connect RStudio to a database, such as SQL.

File importing

We have available different tidyverse-friendly packages to import specific files in your device:

  • The readr package has many functions, like read_csv() or read_delim(), which allow us to import .csv, .txt, tab separated values, etc.

  • If your data is stored in Excel files (.xls or .xlsx), you can use the functions from readxl.


* The haven package lets you load data from other statistical packages, such as SPSS, Stata and SAS.

Database connection

As said before, you can also connect your RStudio directly to a database. It’s straightforward to do so directly in RStudio’s IDE, just follow these simple steps:

  1. Go to the “Connections” tab and click on “New Connection”.

Connections tab

  1. A pop-up window will appear, prompting you to select your desired source.

Select the Data Source

  1. Follow the directions on screen and you’re good to go. You can always click the “Help” button for assistance. If everything went ok, you should be able to see all the tables contained in that DB.

Connecting to an SQL DB


2. Tidy data

All the tidyverse packages rely on having tidy data to work with. But, how can we know if our data is truly tidy? As pointed here, we need to know what values, variables and observations are and have them arranged the right way.

  • Values can be quantitative or qualitative.

  • A variable is a collection of values that measure the same attribute (sales, price, temperature, time).

  • An observation contains all the values measured from all variables in a single unit (a country, a day, a person, a company).

So, now, to consider our data as tidy, we must ensure that:

  1. Every column is one variable (and only one).

  2. Every row is just one observation.

  3. Every cell is a single value.

The tidyr package can help us achieve this by:

  • reshaping data by pivotting (pivot_longer() or pivot_wider()),

  • rectangling, which can handle nested data (such as JSON files) into tidy tibbles (unnest_longer() or unnest_wider()),

  • filling missing values, by replacing them (replace_na()), dropping such observations (drop_na()), or filling with the previous or next values (fill()),

  • splitting and combining character columns (separate() and unite()), among many other things.

3. Understand your data

In order to properly understand our data, we might need to transform it, visualize it, and then model it.

Data transformation

The dplyr package contains functions specifically designed to help you transform tidy data. You can add new variables (mutate()), choose specific variables (select()), pick observations by their values (filter()) or by their position or index in the table (slice()), sort observations (arrange).

You can also group your data (group sales by store: group_by(data, store)).

Whenever you want to manipulate character variables, the stringr package is a great way to deal with the matter. Conveniently, all the stringr functions start with str_. Some of the things you can do with it are:

  • Detect matches, get the index where the pattern is found, count occurences or locate the position of the pattern within the string (str_detect(), `str_which(), str_count(), str_locate()).

  • Subset strings, by extracting substrings from vectors (str_sub()), subsetting a tibble to return only the observations that have the pattern (str_subset()), extract the string pattern (str_extract())…

  • Manage lengths (str_length(), str_pad(), str_trunc(), str_trim()),

  • Mutate, join and split strings, (str_replace(), str_to_lower(), str_c()).

Categorical variables, or Factors, as they’re called in R can be manipulated with the forcats package.

You can have either ordered factors, or unordered factors, and some functions that would help you handle them would be:

  • fct_reorder(), using another variable to specify the new order.

  • fct_infreq(), to order ir according to the frequency of values.

  • fct_relevel(), to manually choose the order.

  • fct_lump(), to collapse the least values of a factor into “other” category.

Handling date-time variables in R can be challenging with base R. Fortunately, the lubridate package is here for you.

For example, if you import an Excel sheet with a date variable on it, it will be parsed as character. You need to convert it to a date (or date-time) variable, in order to make calculations, plots, or anything relevant with it.

There are some very intuitive parsing functions to help you out, such as ymd() for dates stored with the order year month date (it can handle many formats such as “YYYY-MM-DD”, “YYYY/MM/DD”, “YYYY MM DD”, etc.), dmy() for cases when the day comes first, followed by month and year (“DD-MM-YYYY”). Date-time objects can also be parsed with ymd_hms(), ymd_h(), dmy_hm(), hms() just to mention a few.


Proceed to Visualization

Visualization


Base R plots and graphs are very basic (sometimes even ugly):

plot(data = mpg, hwy ~ displ)

Luckily, the ggplot2 package can produce astonishing plots and figures conveniently. It relies on the “Grammar of Graphics”, where you:

  • provide the data,

  • specify how you want to map the variables to aesthetics,

  • what type of plot or graph you want to produce

  • any other customization you’d like,

and ggplot2 takes care of it.

ggplot(mpg, aes(displ, hwy)) + 
  geom_point()

So, to generally describe how a ggplot2 plot works is as follows:

  1. Start with a ggplot() object, where you specify the data to be used,

  2. supply the aesthetic mapping (with aes()),

  3. add on layers:

    • If you want a scatterplot, use geom_point(), histogram geom_hist(). Other common plots are geom_line(), geom_bar(), geom_boxplot().
    • define color scales, such as scale_color_brewer() or scale_color_distiller(),
    • faceting specifications facet_wrap() or facet_grid()
    • coordinate systems, such as coord_cartesian(), coord_flip()

Every element is separated with a plus sign (+):

ggplot(mpg, aes(displ, hwy, colour = class)) +
  geom_point() +
  facet_wrap(~manufacturer)

It’s important to mention that the aesthetics can be passed inside the ggplot() function, or within a graphic primitive. In the former, the aesthetics are the same for all the layers, whereas in the latter, the aesthetics passed to a specific layer only affect that layer.

ggplot(mpg, aes(displ, hwy)) +
  geom_point(aes(color = class)) +
  geom_smooth()


ggplot(mpg, aes(displ, hwy,color = class)) +
  geom_point() +
  geom_smooth()

We could not cover all the different variants that can be achieved with ggplot2 here, even if we tried.


Continue to Exploratory Data Analysis

Exploratory Data Analysis

Visually inspecting your data can give you insight to their dynamics, patterns, and historic behavior. However, before going into the modelling phase of the analysis, we must perform the exploratory data analysis (EDA).

EDA involves making hypothesis regarding your data, transform and visually inspect statistical properties of it.

Some of the most important things to note on the EDA process are:

  • What type of variation do each variable have?

  • What is the covariation between variables?

  • Are there outliers present in the data?

  • What type of distribution do the variables follow?

Some examples

For categorical variables (factors), we can use a bar chart:

ggplot(data = diamonds) +
  geom_bar(mapping = aes(x = cut)) +
  ggtitle("Count of Diamonds by cut quality")

For continuous variables, a histogram can be used:

ggplot(data = diamonds) +
  geom_histogram(mapping = aes(x = carat), binwidth = 0.5) +
  ggtitle("Histogram of carats")

If you want to analyze the histogram of multiple variables in one single plot, you can use geom_freqpoly() or use facetting:

ggplot(data = diamonds %>% filter(carat < 3), mapping = aes(x = carat, colour = cut)) +
  geom_freqpoly(binwidth = 0.1)

ggplot(data = diamonds %>% filter(carat < 3), mapping = aes(x = carat, fill = cut)) +
  geom_histogram(binwidth = 0.1) +
  facet_wrap(~ cut) +
  theme(legend.position = "none")

Choosing the binwidth of the histogram can tell different stories or can reveal different patterns:

g <- ggplot(data = diamonds %>% filter(carat < 3), mapping = aes(x = carat))
g0 <- g + geom_histogram(binwidth = 0.5) +
  ggtitle("Binwidth = 0.5")
g1 <- g + geom_histogram(binwidth = 0.1) +
  ggtitle("Binwidth = 0.1")
g2 <- g + geom_histogram(binwidth = 0.01) +
  ggtitle("Binwidth = 0.01")
g3 <- g0/g1/g2
g3 + plot_annotation(title = "Histograms varying the binwidth",
                     subtitle = "Different patterns can arise when selecting different binwidths")

Another option is to go with boxplots:

ggplot(data = mpg) +
  geom_boxplot(mapping = aes(x = reorder(class, hwy, FUN = median), y = hwy))+ labs(x = "class", y = "hwy mpg")

Again, these are just some examples, but the list of goes on and on.


Go to Model

Model

Whenever we try to model a variable or phenomenom, it is said that we are trying to get a simplified version of reality. In fact, it’s simpler than that. What we are really trying to do is understand the way a variable or set of variables change, while ignoring or eliminating external “noise”.

It is well known to most data scientists that you cannot use the same data for modelling and testing (or forecasting). That’s way many people split their data into a training and testing set. However, a true data scientist has to be even stricter on its use of data:

It is recommended that you split your data into three sets:

  1. ~ 60% of your data goes to the training set. Here, you can visualize it, perform all the model fitting and tweaking you want, over and over again.

  2. ~ 20% of your data should go to a query set. With this query set, you can compare models by hand and visualize the outcomes.

  3. The ~ 20% remaining data would conform the test set. Once you’ve compared all your models with the training and/or query sets, you can test your final model. This test can only be performed ONCE. This ensures no bias is introduced in the model and it remains a true forecast.

We will go through many different models aimed to provide forecasts for different situations.


Go to Forecasting

4. Forecasting

forecast






Examples

data("world_bank_pop")
# A tibble: 1,056 x 20
#    country indicator `2000` `2001` `2002` `2003`  `2004`  `2005`   `2006`   `2007`
#    <chr>   <chr>      <dbl>  <dbl>  <dbl>  <dbl>   <dbl>   <dbl>    <dbl>    <dbl>
#  1 ABW     SP.URB.T~ 4.24e4 4.30e4 4.37e4 4.42e4 4.47e+4 4.49e+4  4.49e+4  4.47e+4
#  2 ABW     SP.URB.G~ 1.18e0 1.41e0 1.43e0 1.31e0 9.51e-1 4.91e-1 -1.78e-2 -4.35e-1
#  3 ABW     SP.POP.T~ 9.09e4 9.29e4 9.50e4 9.70e4 9.87e+4 1.00e+5  1.01e+5  1.01e+5
#  4 ABW     SP.POP.G~ 2.06e0 2.23e0 2.23e0 2.11e0 1.76e+0 1.30e+0  7.98e-1  3.84e-1
#  5 AFG     SP.URB.T~ 4.44e6 4.65e6 4.89e6 5.16e6 5.43e+6 5.69e+6  5.93e+6  6.15e+6
#  6 AFG     SP.URB.G~ 3.91e0 4.66e0 5.13e0 5.23e0 5.12e+0 4.77e+0  4.12e+0  3.65e+0
#  7 AFG     SP.POP.T~ 2.01e7 2.10e7 2.20e7 2.31e7 2.41e+7 2.51e+7  2.59e+7  2.66e+7
#  8 AFG     SP.POP.G~ 3.49e0 4.25e0 4.72e0 4.82e0 4.47e+0 3.87e+0  3.23e+0  2.76e+0
#  9 AGO     SP.URB.T~ 8.23e6 8.71e6 9.22e6 9.77e6 1.03e+7 1.09e+7  1.15e+7  1.21e+7
# 10 AGO     SP.URB.G~ 5.44e0 5.59e0 5.70e0 5.76e0 5.75e+0 5.69e+0  4.92e+0  4.89e+0
# ... with 1,046 more rows, and 10 more variables: `2008` <dbl>, `2009` <dbl>,
#   `2010` <dbl>, `2011` <dbl>, `2012` <dbl>, `2013` <dbl>, `2014` <dbl>,
#   `2015` <dbl>, `2016` <dbl>, `2017` <dbl>

  • Programación
    • Para llevar a cabo iteraciones en objetos de R, es conveniente utilizar funciones de la paquetería purrr.
LS0tDQp0aXRsZTogIlIgJiAqKmB0aWR5dmVyc2VgKiogLSAxMDEiDQphdXRob3I6ICJQYWJsbyBCZW5hdmlkZXMtSGVycmVyYSINCmRhdGU6ICIyMDIwLTA1LTIwIg0Kb3V0cHV0OiANCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0b2M6IFRSVUUNCiAgICB0b2NfZmxvYXQ6IFRSVUUNCiAgICB0aGVtZTogZGFya2x5DQogICAgaGlnaGxpZ2h0OiB0YW5nbw0KLS0tDQoNCmBgYHtjc3MgQ1NTIHN0eWxlcywgZWNobz1GQUxTRX0NCmNvZGUgew0KICBiYWNrZ3JvdW5kLWNvbG9yOiByZ2JhKDEwNSwgMTA1LCAxMDUsMC41KTsNCn0NCmgxIHsNCiAgY29sb3I6IGFxdWFtYXJpbmUgIWltcG9ydGFudA0KfQ0KaDIgew0KICBjb2xvcjogZGVlcHNreWJsdWUgIWltcG9ydGFudA0KfQ0KaDMgew0KICBjb2xvcjogdG9tYXRvICFpbXBvcnRhbnQNCn0NCmBgYA0KDQoNCg0KDQoNCiFbXShpbWFnZXMvcl90aWR5dmVyc2UuanBnKQ0KDQojIFByZXJlcXVpc2l0ZXMNCg0KRm9yIHRoaXMgY291cnNlLCB5b3Ugd2lsbCBuZWVkIHRvIGRvd25sb2FkIFIgYW5kIFJTdHVkaW8uIE9uZSB3YXkgdG8gZ2V0IFIgaXMgdG8gZ28gdG8gW0NSQU5dKGh0dHBzOi8vY2xvdWQuci1wcm9qZWN0Lm9yZy8pICgqKkMqKm9tcHJlaGVuc2l2ZSAqKlIqKiBhcmNoaXZlICoqTioqZXR3b3JrKS4NCg0KIVtdKGltYWdlcy9SU3R1ZGlvLnBuZyl7d2lkdGg9MTUlfQ0KDQpSU3R1ZGlvIGlzIHRoZSBJREUgKGludGVncmF0ZWQgZGV2ZWxvcG1lbnQgZW52aXJvbm1lbnQpIHdlJ2xsIGJlIHVzaW5nLiBZb3UgY2FuIGRvd25sb2FkIGl0IGZyb20gaHR0cDovL3d3dy5yc3R1ZGlvLmNvbS9kb3dubG9hZC4gDQoNCldlIHdpbGwgYWxzbyB1c2UgdGhlIGBwYXRjaHdvcmtgIHBhY2thZ2UgdG8gZWFzaWx5IGFycmFuZ2UgbWFueSBwbG90cyBpbnRvIG9uZSBzaW5nbGUgZmlndXJlLg0KDQojIFdoYXQgaXMgdGhlIHRpZHl2ZXJzZT8geyN0aWR5dmVyc2V9DQoNCiJUaGUgW2B0aWR5dmVyc2VgXShodHRwczovL3d3dy50aWR5dmVyc2Uub3JnLykgaXMgYW4gb3BpbmlvbmF0ZWQgY29sbGVjdGlvbiBvZiBSIHBhY2thZ2VzIGRlc2lnbmVkIGZvciBkYXRhIHNjaWVuY2UuIEFsbCBwYWNrYWdlcyBzaGFyZSBhbiB1bmRlcmx5aW5nIGRlc2lnbiBwaGlsb3NvcGh5LCBncmFtbWFyLCBhbmQgZGF0YSBzdHJ1Y3R1cmVzLiINCg0KVGhlc2UgcGFja2FnZXMgaGVscCB5b3UgaW1wb3J0LCB0aWR5IGFuZCB1bmRlcnN0YW5kIGRhdGEsIHRvIGZpbmFsbHkgYmUgYWJsZSB0byBjb21tdW5pY2F0ZSB5b3VyIGZpbmRpbmdzIGluIGFuIGVhc3kgd2F5Lg0KDQpZb3UgY2FuIGluc3RhbGwgdGhlIGNvcmUgcGFja2FnZXMgZnJvbSB0aGUgYHRpZHl2ZXJzZWAgYnkgc2ltcGx5IHR5cGluZyBgaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIilgIGluIHRoZSBjb25zb2xlLg0KDQpgYGB7ciBpbnN0YWxsIHRpZHl2ZXJzZSwgZXZhbD1GQUxTRX0NCiMgWW91IG9ubHkgbmVlZCB0byBpbnN0YWxsIGEgcGFja2FnZSBvbmNlIHBlciBkZXZpY2UNCmluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpDQpgYGANCg0KVGhlIGNvcmUgYHRpZHl2ZXJzZWAgcGFja2FnZXMgKGBnZ3Bsb3QyYCwgYHRpYmJsZWAsIGB0aWR5cmAsIGByZWFkcmAsIGBkcGx5cmAsIGBzdHJpbmdyYCwgYGZvcmNhdHNgLCBgcHVycnJgKSBjYW4gYmUgbG9hZGVkIHRoZW4gd2l0aCB0aGUgYGxpYnJhcnkoKWAgZnVuY3Rpb24uDQoNCmBgYHtyIHBrZ3N9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkocGF0Y2h3b3JrKQ0KYGBgDQoNClRoZXJlIGFyZSBtYW55IG90aGVyIHBhY2thZ2VzIHRoYXQgY29tcGx5IHdpdGggdGhlIGB0aWR5dmVyc2VgIGRhdGEgc3RydWN0dXJlIGFuZCBwcmFjdGljZXMgdGhhdCBoYXZlIHRvIGJlIGluc3RhbGxlZCBhbmQgbG9hZGVkIGluZGVwZW5kZW50bHkuIFNvbWUgb2YgdGhvc2UgdGhhdCB3ZSdsbCBiZSB1c2luZyB0aHJvdWdob3V0IHRoZSBjb3Vyc2UgYXJlIGB0c2liYmxlYCwgYGZhYmxlYCwgYHRpZHltb2RlbHNgLg0KDQohW10oaW1hZ2VzL3RpZHl2ZXJzZS5wbmcpDQpTbywgaG93IGRvZXMgZWFjaCBvZiB0aGVzZSBwYWNrYWdlcyBoZWxwIHVzIHRhY2tsZSBvdXIgZGF0YSBzY2llbmNlIHByb2JsZW1zPw0KDQojIERhdGEgc2NpZW5jZSB3b3JrZmxvdyB7I3dvcmtmbG93IC50YWJzZXQgLnRhYnNldC1mYWRlfQ0KDQpXZSBjYW4gc2F5IHRoYXQgYSBkYXRhIHNjaWVuY2Ugd29ya2Zsb3cgKGFpbWVkIGF0IGZvcmVjYXN0aW5nKSBjb21wcmlzZXMgdGhlIGZvbGxvd2luZyBnZW5lcmFsIHN0ZXBzOg0KDQojIyAxLiBJbXBvcnQgZGF0YSB7LnRhYnNldCAudGFic2V0LXBpbGxzfQ0KDQpEYXRhIGNhbiBiZSBpbXBvcnRlZCBpbnRvIFIgaW4gbWFueSB3YXlzLiBUaGUgZWFzaWVzdCB3YXkgaXMgaW1wb3J0aW5nIGZpbGVzLCBzdWNoIGFzIC5jc3YsIC50eHQgb3IgTVMgRXhjZWwgZmlsZXMuIEhvd2V2ZXIsIHlvdSBjYW4gYWxzbyBpbXBvcnQgZmlsZXMgZnJvbSBvdGhlciBzdGF0aXN0aWNhbCBzb2Z0d2FyZXMgb3IgZXZlbiBjb25uZWN0IFJTdHVkaW8gdG8gYSBkYXRhYmFzZSwgc3VjaCBhcyBTUUwuDQoNCiMjIyBGaWxlIGltcG9ydGluZw0KDQpXZSBoYXZlIGF2YWlsYWJsZSBkaWZmZXJlbnQgdGlkeXZlcnNlLWZyaWVuZGx5IHBhY2thZ2VzIHRvIGltcG9ydCBzcGVjaWZpYyBmaWxlcyBpbiB5b3VyIGRldmljZToNCg0KIVtdKGltYWdlcy9yZWFkci5wbmcpe3dpZHRoPTE1JX0NCg0KICAqIFRoZSBbYHJlYWRyYF0oaHR0cHM6Ly9yZWFkci50aWR5dmVyc2Uub3JnLykgcGFja2FnZSBoYXMgbWFueSBmdW5jdGlvbnMsIGxpa2UgYHJlYWRfY3N2KClgIG9yIGByZWFkX2RlbGltKClgLCB3aGljaCBhbGxvdyB1cyB0byBpbXBvcnQgLmNzdiwgLnR4dCwgdGFiIHNlcGFyYXRlZCB2YWx1ZXMsIGV0Yy4NCg0KIVtdKGltYWdlcy9yZWFkeGwucG5nKXt3aWR0aD0xNSV9DQogICAgDQogICogSWYgeW91ciBkYXRhIGlzIHN0b3JlZCBpbiBFeGNlbCBmaWxlcyAoLnhscyBvciAueGxzeCksIHlvdSBjYW4gdXNlIHRoZSBmdW5jdGlvbnMgZnJvbSBbYHJlYWR4bGBdKGh0dHBzOi8vcmVhZHhsLnRpZHl2ZXJzZS5vcmcvKS4NCg0KIVtdKGltYWdlcy9oYXZlbi5wbmcpe3dpZHRoPTE1JX0gICAgDQogICogVGhlIFtgaGF2ZW5gXShodHRwczovL2hhdmVuLnRpZHl2ZXJzZS5vcmcvKSBwYWNrYWdlIGxldHMgeW91IGxvYWQgZGF0YSBmcm9tIG90aGVyIHN0YXRpc3RpY2FsIHBhY2thZ2VzLCBzdWNoIGFzIFNQU1MsIFN0YXRhIGFuZCBTQVMuDQogIA0KIyMjIERhdGFiYXNlIGNvbm5lY3Rpb24NCg0KQXMgc2FpZCBiZWZvcmUsIHlvdSBjYW4gYWxzbyBjb25uZWN0IHlvdXIgUlN0dWRpbyBkaXJlY3RseSB0byBhIGRhdGFiYXNlLiBJdCdzIHN0cmFpZ2h0Zm9yd2FyZCB0byBkbyBzbyBkaXJlY3RseSBpbiBSU3R1ZGlvJ3MgSURFLCBqdXN0IGZvbGxvdyB0aGVzZSBzaW1wbGUgc3RlcHM6DQoNCjEuIEdvIHRvIHRoZSAiKipDb25uZWN0aW9ucyoqIiB0YWIgYW5kIGNsaWNrIG9uICoqIk5ldyBDb25uZWN0aW9uIioqLg0KDQohWypDb25uZWN0aW9ucyB0YWIqXShpbWFnZXMvY29ubmVjdGlvbnMuUE5HKXt3aWR0aD05MCV9DQoNCjIuIEEgcG9wLXVwIHdpbmRvdyB3aWxsIGFwcGVhciwgcHJvbXB0aW5nIHlvdSB0byBzZWxlY3QgeW91ciBkZXNpcmVkIHNvdXJjZS4NCg0KIVsqU2VsZWN0IHRoZSBEYXRhIFNvdXJjZSpdKGltYWdlcy9jb25uZWN0aW9ucyBwb3B1cC5QTkcpe3dpZHRoPTYwJX0NCg0KMy4gRm9sbG93IHRoZSBkaXJlY3Rpb25zIG9uIHNjcmVlbiBhbmQgeW91J3JlIGdvb2QgdG8gZ28uIFlvdSBjYW4gYWx3YXlzIGNsaWNrIHRoZSAiSGVscCIgYnV0dG9uIGZvciBhc3Npc3RhbmNlLiBJZiBldmVyeXRoaW5nIHdlbnQgb2ssIHlvdSBzaG91bGQgYmUgYWJsZSB0byBzZWUgYWxsIHRoZSB0YWJsZXMgY29udGFpbmVkIGluIHRoYXQgREIuDQoNCiFbKkNvbm5lY3RpbmcgdG8gYW4gU1FMIERCKl0oaW1hZ2VzL2Nvbm5lY3Rpb24gc3FsLlBORyl7d2lkdGg9NzAlfQ0KDQo8YnI+DQoNCiMjIDIuIFRpZHkgZGF0YSB7I3RpZHl9DQoNCkFsbCB0aGUgYHRpZHl2ZXJzZWAgcGFja2FnZXMgcmVseSBvbiBoYXZpbmcgdGlkeSBkYXRhIHRvIHdvcmsgd2l0aC4gQnV0LCBob3cgY2FuIHdlIGtub3cgaWYgb3VyIGRhdGEgaXMgdHJ1bHkgdGlkeT8gQXMgcG9pbnRlZCBbaGVyZV0oaHR0cHM6Ly90aWR5ci50aWR5dmVyc2Uub3JnL2FydGljbGVzL3RpZHktZGF0YS5odG1sKSwgd2UgbmVlZCB0byBrbm93IHdoYXQgKip2YWx1ZXMsIHZhcmlhYmxlcyBhbmQgb2JzZXJ2YXRpb25zKiogYXJlIGFuZCBoYXZlIHRoZW0gYXJyYW5nZWQgdGhlIHJpZ2h0IHdheS4NCiAgDQogICogKipWYWx1ZXMqKiBjYW4gYmUgcXVhbnRpdGF0aXZlIG9yIHF1YWxpdGF0aXZlLg0KICAgIA0KICAqIEEgKip2YXJpYWJsZSoqIGlzIGEgY29sbGVjdGlvbiBvZiAqdmFsdWVzKiB0aGF0IG1lYXN1cmUgdGhlIHNhbWUgYXR0cmlidXRlIChzYWxlcywgcHJpY2UsIHRlbXBlcmF0dXJlLCB0aW1lKS4NCiAgICANCiAgKiBBbiAqKm9ic2VydmF0aW9uKiogY29udGFpbnMgYWxsIHRoZSB2YWx1ZXMgbWVhc3VyZWQgZnJvbSBhbGwgdmFyaWFibGVzIGluIGEgc2luZ2xlIHVuaXQgKGEgY291bnRyeSwgYSBkYXksIGEgcGVyc29uLCBhIGNvbXBhbnkpLg0KICAgIA0KU28sIG5vdywgdG8gY29uc2lkZXIgb3VyIGRhdGEgYXMgKip0aWR5KiosIHdlIG11c3QgZW5zdXJlIHRoYXQ6DQogICAgDQogIGkpIEV2ZXJ5IGNvbHVtbiBpcyBvbmUgdmFyaWFibGUgKGFuZCBvbmx5IG9uZSkuDQogICAgDQogIGlpKSBFdmVyeSByb3cgaXMganVzdCBvbmUgb2JzZXJ2YXRpb24uDQogICAgDQogIGlpaSkgRXZlcnkgY2VsbCBpcyBhIHNpbmdsZSB2YWx1ZS4NCg0KIVtdKGltYWdlcy90aWR5ci5wbmcpe3dpZHRoPTE1JX0NCg0KVGhlIFtgdGlkeXJgXShodHRwczovL3RpZHlyLnRpZHl2ZXJzZS5vcmcvKSBwYWNrYWdlIGNhbiBoZWxwIHVzIGFjaGlldmUgdGhpcyBieToNCg0KKiAgICoqcmVzaGFwaW5nKiogZGF0YSBieSBwaXZvdHRpbmcgKGBwaXZvdF9sb25nZXIoKWAgb3IgYHBpdm90X3dpZGVyKClgKSwNCg0KKiAgICoqcmVjdGFuZ2xpbmcqKiwgd2hpY2ggY2FuIGhhbmRsZSBuZXN0ZWQgZGF0YSAoc3VjaCBhcyBKU09OIGZpbGVzKSBpbnRvIHRpZHkgdGliYmxlcyAoYHVubmVzdF9sb25nZXIoKWAgb3IgYHVubmVzdF93aWRlcigpYCksIA0KDQoqICAgKipmaWxsaW5nIG1pc3NpbmcgdmFsdWVzKiosIGJ5IHJlcGxhY2luZyB0aGVtIChgcmVwbGFjZV9uYSgpYCksIGRyb3BwaW5nIHN1Y2ggb2JzZXJ2YXRpb25zIChgZHJvcF9uYSgpYCksIG9yIGZpbGxpbmcgd2l0aCB0aGUgcHJldmlvdXMgb3IgbmV4dCB2YWx1ZXMgKGBmaWxsKClgKSwNCg0KKiAqKnNwbGl0dGluZyBhbmQgY29tYmluaW5nIGNoYXJhY3RlciBjb2x1bW5zKiogKGBzZXBhcmF0ZSgpYCBhbmQgYHVuaXRlKClgKSwgYW1vbmcgbWFueSBvdGhlciB0aGluZ3MuDQoNCg0KIyMgMy4gVW5kZXJzdGFuZCB5b3VyIGRhdGEgeyN1bmRlcnN0YW5kIC50YWJzZXQgLnRhYnNldC1waWxsc30NCg0KSW4gb3JkZXIgdG8gcHJvcGVybHkgdW5kZXJzdGFuZCBvdXIgZGF0YSwgd2UgbWlnaHQgbmVlZCB0byAqKnRyYW5zZm9ybSoqIGl0LCAqKnZpc3VhbGl6ZSoqIGl0LCBhbmQgdGhlbiAqKm1vZGVsKiogaXQuDQoNCg0KIyMjIERhdGEgdHJhbnNmb3JtYXRpb24geyNkYXRhX3RyYW5zZm9ybWF0aW9ufQ0KDQohW10oaW1hZ2VzL2RwbHlyLnBuZyl7d2lkdGg9MTUlfQ0KDQpUaGUgW2BkcGx5cmBdKGh0dHBzOi8vZHBseXIudGlkeXZlcnNlLm9yZy8pIHBhY2thZ2UgY29udGFpbnMgZnVuY3Rpb25zIHNwZWNpZmljYWxseSBkZXNpZ25lZCB0byBoZWxwIHlvdSB0cmFuc2Zvcm0gdGlkeSBkYXRhLiBZb3UgY2FuIGFkZCBuZXcgdmFyaWFibGVzIChgbXV0YXRlKClgKSwgY2hvb3NlIHNwZWNpZmljIHZhcmlhYmxlcyAoYHNlbGVjdCgpYCksIHBpY2sgb2JzZXJ2YXRpb25zIGJ5IHRoZWlyIHZhbHVlcyAoYGZpbHRlcigpYCkgb3IgYnkgdGhlaXIgcG9zaXRpb24gb3IgaW5kZXggaW4gdGhlIHRhYmxlIChgc2xpY2UoKWApLCBzb3J0IG9ic2VydmF0aW9ucyAoYGFycmFuZ2VgKS4NCg0KWW91IGNhbiBhbHNvIGdyb3VwIHlvdXIgZGF0YSAoZ3JvdXAgc2FsZXMgYnkgc3RvcmU6IGBncm91cF9ieShkYXRhLCBzdG9yZSlgKS4NCg0KIVtdKGltYWdlcy9zdHJpbmdyLnBuZyl7d2lkdGg9MTUlfQ0KDQpXaGVuZXZlciB5b3Ugd2FudCB0byBtYW5pcHVsYXRlIGNoYXJhY3RlciB2YXJpYWJsZXMsIHRoZSBbYHN0cmluZ3JgXShodHRwczovL3N0cmluZ3IudGlkeXZlcnNlLm9yZy8pIHBhY2thZ2UgaXMgYSBncmVhdCB3YXkgdG8gZGVhbCB3aXRoIHRoZSBtYXR0ZXIuIENvbnZlbmllbnRseSwgYWxsIHRoZSBgc3RyaW5ncmAgZnVuY3Rpb25zIHN0YXJ0IHdpdGggYHN0cl9gLiBTb21lIG9mIHRoZSB0aGluZ3MgeW91IGNhbiBkbyB3aXRoIGl0IGFyZToNCg0KKiAqKkRldGVjdCBtYXRjaGVzKiosIGdldCB0aGUgaW5kZXggd2hlcmUgdGhlIHBhdHRlcm4gaXMgZm91bmQsIGNvdW50IG9jY3VyZW5jZXMgb3IgbG9jYXRlIHRoZSBwb3NpdGlvbiBvZiB0aGUgcGF0dGVybiB3aXRoaW4gdGhlIHN0cmluZyAoYHN0cl9kZXRlY3QoKWAsIGBgc3RyX3doaWNoKClgLCBgc3RyX2NvdW50KClgLCBgc3RyX2xvY2F0ZSgpYCkuDQoNCiogKipTdWJzZXQgc3RyaW5ncyoqLCBieSBleHRyYWN0aW5nIHN1YnN0cmluZ3MgZnJvbSB2ZWN0b3JzIChgc3RyX3N1YigpYCksIHN1YnNldHRpbmcgYSB0aWJibGUgdG8gcmV0dXJuIG9ubHkgdGhlIG9ic2VydmF0aW9ucyB0aGF0IGhhdmUgdGhlIHBhdHRlcm4gKGBzdHJfc3Vic2V0KClgKSwgZXh0cmFjdCB0aGUgc3RyaW5nIHBhdHRlcm4gKGBzdHJfZXh0cmFjdCgpYCkuLi4NCg0KKiAqKk1hbmFnZSBsZW5ndGhzKiogKGBzdHJfbGVuZ3RoKClgLCBgc3RyX3BhZCgpYCwgYHN0cl90cnVuYygpYCwgYHN0cl90cmltKClgKSwNCg0KKiAqKk11dGF0ZSwgam9pbiBhbmQgc3BsaXQqKiBzdHJpbmdzLCAoYHN0cl9yZXBsYWNlKClgLCBgc3RyX3RvX2xvd2VyKClgLCBgc3RyX2MoKWApLg0KDQohW10oaW1hZ2VzL2ZvcmNhdHMucG5nKXt3aWR0aD0xNSV9DQoNCkNhdGVnb3JpY2FsIHZhcmlhYmxlcywgb3IgKipGYWN0b3JzKiosIGFzIHRoZXkncmUgY2FsbGVkIGluICoqUioqIGNhbiBiZSBtYW5pcHVsYXRlZCB3aXRoIHRoZSBbYGZvcmNhdHNgXShodHRwczovL2ZvcmNhdHMudGlkeXZlcnNlLm9yZy8pIHBhY2thZ2UuDQoNCllvdSBjYW4gaGF2ZSBlaXRoZXIgb3JkZXJlZCBmYWN0b3JzLCBvciB1bm9yZGVyZWQgZmFjdG9ycywgYW5kIHNvbWUgZnVuY3Rpb25zIHRoYXQgd291bGQgaGVscCB5b3UgaGFuZGxlIHRoZW0gd291bGQgYmU6DQoNCiogYGZjdF9yZW9yZGVyKClgLCB1c2luZyBhbm90aGVyIHZhcmlhYmxlIHRvIHNwZWNpZnkgdGhlIG5ldyBvcmRlci4NCg0KKiBgZmN0X2luZnJlcSgpYCwgdG8gb3JkZXIgaXIgYWNjb3JkaW5nIHRvIHRoZSBmcmVxdWVuY3kgb2YgdmFsdWVzLg0KDQoqIGBmY3RfcmVsZXZlbCgpYCwgdG8gbWFudWFsbHkgY2hvb3NlIHRoZSBvcmRlci4NCg0KKiBgZmN0X2x1bXAoKWAsIHRvIGNvbGxhcHNlIHRoZSBsZWFzdCB2YWx1ZXMgb2YgYSBmYWN0b3IgaW50byAib3RoZXIiIGNhdGVnb3J5Lg0KDQoNCiFbXShpbWFnZXMvbHVicmlkYXRlLnBuZyl7d2lkdGg9MTUlfQ0KDQoNCkhhbmRsaW5nIGRhdGUtdGltZSB2YXJpYWJsZXMgaW4gUiBjYW4gYmUgY2hhbGxlbmdpbmcgd2l0aCBiYXNlIFIuIEZvcnR1bmF0ZWx5LCB0aGUgW2BsdWJyaWRhdGVgXShodHRwczovL2x1YnJpZGF0ZS50aWR5dmVyc2Uub3JnLykgcGFja2FnZSBpcyBoZXJlIGZvciB5b3UuDQoNCkZvciBleGFtcGxlLCBpZiB5b3UgaW1wb3J0IGFuIEV4Y2VsIHNoZWV0IHdpdGggYSBkYXRlIHZhcmlhYmxlIG9uIGl0LCBpdCB3aWxsIGJlIHBhcnNlZCBhcyBjaGFyYWN0ZXIuIFlvdSBuZWVkIHRvIGNvbnZlcnQgaXQgdG8gYSBkYXRlIChvciBkYXRlLXRpbWUpIHZhcmlhYmxlLCBpbiBvcmRlciB0byBtYWtlIGNhbGN1bGF0aW9ucywgcGxvdHMsIG9yIGFueXRoaW5nIHJlbGV2YW50IHdpdGggaXQuDQoNClRoZXJlIGFyZSBzb21lIHZlcnkgaW50dWl0aXZlIHBhcnNpbmcgZnVuY3Rpb25zIHRvIGhlbHAgeW91IG91dCwgc3VjaCBhcyBgeW1kKClgIGZvciBkYXRlcyBzdG9yZWQgd2l0aCB0aGUgb3JkZXIgeWVhciBtb250aCBkYXRlIChpdCBjYW4gaGFuZGxlIG1hbnkgZm9ybWF0cyBzdWNoIGFzICJZWVlZLU1NLUREIiwgIllZWVkvTU0vREQiLCAiWVlZWSBNTSBERCIsIGV0Yy4pLCBgZG15KClgIGZvciBjYXNlcyB3aGVuIHRoZSBkYXkgY29tZXMgZmlyc3QsIGZvbGxvd2VkIGJ5IG1vbnRoIGFuZCB5ZWFyICgiREQtTU0tWVlZWSIpLiBEYXRlLXRpbWUgb2JqZWN0cyBjYW4gYWxzbyBiZSBwYXJzZWQgd2l0aCBgeW1kX2htcygpYCwgYHltZF9oKClgLCBgZG15X2htKClgLCBgaG1zKClgIGp1c3QgdG8gbWVudGlvbiBhIGZldy4gDQoNCjxicj4NCg0KPGZvbnQgc2l6ZT0iNSI+IFByb2NlZWQgdG8gW1Zpc3VhbGl6YXRpb25dKCN1bmRlcnN0YW5kKSA8L2ZvbnQ+IA0KDQoNCiMjIyBWaXN1YWxpemF0aW9uIHsjdmlzdWFsaXphdGlvbn0NCg0KPGJyPg0KDQpCYXNlICoqUioqIHBsb3RzIGFuZCBncmFwaHMgYXJlIHZlcnkgYmFzaWMgKHNvbWV0aW1lcyBldmVuIHVnbHkpOg0KDQpgYGB7ciBiYXNlIHIgcGxvdCwgZWNobz1UUlVFfQ0KcGxvdChkYXRhID0gbXBnLCBod3kgfiBkaXNwbCkNCmBgYA0KDQohW10oaW1hZ2VzL2dncGxvdDIucG5nKXt3aWR0aD0xNSV9DQoNCkx1Y2tpbHksIHRoZSBbYGdncGxvdDJgXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy8pIHBhY2thZ2UgY2FuIHByb2R1Y2UgYXN0b25pc2hpbmcgcGxvdHMgYW5kIGZpZ3VyZXMgY29udmVuaWVudGx5LiBJdCByZWxpZXMgb24gdGhlICJHcmFtbWFyIG9mIEdyYXBoaWNzIiwgd2hlcmUgeW91Og0KDQoqIHByb3ZpZGUgdGhlIGRhdGEsDQoNCiogc3BlY2lmeSBob3cgeW91IHdhbnQgdG8gbWFwIHRoZSB2YXJpYWJsZXMgdG8gYWVzdGhldGljcywNCg0KKiB3aGF0IHR5cGUgb2YgcGxvdCBvciBncmFwaCB5b3Ugd2FudCB0byBwcm9kdWNlDQoNCiogYW55IG90aGVyIGN1c3RvbWl6YXRpb24geW91J2QgbGlrZSwNCg0KYW5kIGBnZ3Bsb3QyYCB0YWtlcyBjYXJlIG9mIGl0Lg0KDQpgYGB7ciBiYXNpYyBnZ3Bsb3QsIGZpZy5hbGlnbj0nY2VudGVyJ30NCmdncGxvdChtcGcsIGFlcyhkaXNwbCwgaHd5KSkgKyANCiAgZ2VvbV9wb2ludCgpDQpgYGANCg0KU28sIHRvIGdlbmVyYWxseSBkZXNjcmliZSBob3cgYSBgZ2dwbG90MmAgcGxvdCB3b3JrcyBpcyBhcyBmb2xsb3dzOg0KDQoxLiBTdGFydCB3aXRoIGEgYGdncGxvdCgpYCBvYmplY3QsIHdoZXJlIHlvdSBzcGVjaWZ5IHRoZSBkYXRhIHRvIGJlIHVzZWQsDQoNCjIuIHN1cHBseSB0aGUgYWVzdGhldGljIG1hcHBpbmcgKHdpdGggYGFlcygpYCksDQoNCjMuIGFkZCBvbiBsYXllcnM6DQogICAgKiBJZiB5b3Ugd2FudCBhIHNjYXR0ZXJwbG90LCB1c2UgYGdlb21fcG9pbnQoKWAsIGhpc3RvZ3JhbSBgZ2VvbV9oaXN0KClgLiBPdGhlciBjb21tb24gcGxvdHMgYXJlIGBnZW9tX2xpbmUoKWAsIGBnZW9tX2JhcigpYCwgYGdlb21fYm94cGxvdCgpYC4NCiAgICAqIGRlZmluZSBjb2xvciBzY2FsZXMsIHN1Y2ggYXMgYHNjYWxlX2NvbG9yX2JyZXdlcigpYCBvciBgc2NhbGVfY29sb3JfZGlzdGlsbGVyKClgLA0KICAgICogZmFjZXRpbmcgc3BlY2lmaWNhdGlvbnMgYGZhY2V0X3dyYXAoKWAgb3IgYGZhY2V0X2dyaWQoKWANCiAgICAqIGNvb3JkaW5hdGUgc3lzdGVtcywgc3VjaCBhcyBgY29vcmRfY2FydGVzaWFuKClgLCBgY29vcmRfZmxpcCgpYA0KDQpFdmVyeSBlbGVtZW50IGlzIHNlcGFyYXRlZCB3aXRoIGEgcGx1cyBzaWduICgqKisqKik6DQoNCmBgYHtyIGN1c3RvbSBnZ3Bsb3QsIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTh9DQpnZ3Bsb3QobXBnLCBhZXMoZGlzcGwsIGh3eSwgY29sb3VyID0gY2xhc3MpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGZhY2V0X3dyYXAofm1hbnVmYWN0dXJlcikNCmBgYA0KDQpJdCdzIGltcG9ydGFudCB0byBtZW50aW9uIHRoYXQgdGhlIGFlc3RoZXRpY3MgY2FuIGJlIHBhc3NlZCBpbnNpZGUgdGhlIGBnZ3Bsb3QoKWAgZnVuY3Rpb24sIG9yIHdpdGhpbiBhIGdyYXBoaWMgcHJpbWl0aXZlLiBJbiB0aGUgZm9ybWVyLCB0aGUgYWVzdGhldGljcyBhcmUgdGhlIHNhbWUgZm9yIGFsbCB0aGUgbGF5ZXJzLCB3aGVyZWFzIGluIHRoZSBsYXR0ZXIsIHRoZSBhZXN0aGV0aWNzIHBhc3NlZCB0byBhIHNwZWNpZmljIGxheWVyIG9ubHkgYWZmZWN0IHRoYXQgbGF5ZXIuDQoNCmBgYHtyIGdncGxvdCBhZXMsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGZpZy5hbGlnbj0nY2VudGVyJ30NCmdncGxvdChtcGcsIGFlcyhkaXNwbCwgaHd5KSkgKw0KICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGNsYXNzKSkgKw0KICBnZW9tX3Ntb290aCgpDQoNCmdncGxvdChtcGcsIGFlcyhkaXNwbCwgaHd5LGNvbG9yID0gY2xhc3MpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21fc21vb3RoKCkNCmBgYA0KDQoNCldlIGNvdWxkIG5vdCBjb3ZlciBhbGwgdGhlIGRpZmZlcmVudCB2YXJpYW50cyB0aGF0IGNhbiBiZSBhY2hpZXZlZCB3aXRoIGBnZ3Bsb3QyYCBoZXJlLCBldmVuIGlmIHdlIHRyaWVkLg0KDQo8YnI+DQoNCjxmb250IHNpemU9IjUiPiBDb250aW51ZSB0byBbRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpc10oI3VuZGVyc3RhbmQpIDwvZm9udD4gDQoNCiMjIyBFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzIHsjRURBfQ0KDQpWaXN1YWxseSBpbnNwZWN0aW5nIHlvdXIgZGF0YSBjYW4gZ2l2ZSB5b3UgaW5zaWdodCB0byB0aGVpciBkeW5hbWljcywgcGF0dGVybnMsIGFuZCBoaXN0b3JpYyBiZWhhdmlvci4gSG93ZXZlciwgYmVmb3JlIGdvaW5nIGludG8gdGhlIG1vZGVsbGluZyBwaGFzZSBvZiB0aGUgYW5hbHlzaXMsIHdlIG11c3QgcGVyZm9ybSB0aGUgKipleHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzIChFREEpKiouDQoNCkVEQSBpbnZvbHZlcyBtYWtpbmcgaHlwb3RoZXNpcyByZWdhcmRpbmcgeW91ciBkYXRhLCB0cmFuc2Zvcm0gYW5kIHZpc3VhbGx5IGluc3BlY3Qgc3RhdGlzdGljYWwgcHJvcGVydGllcyBvZiBpdC4NCg0KU29tZSBvZiB0aGUgbW9zdCBpbXBvcnRhbnQgdGhpbmdzIHRvIG5vdGUgb24gdGhlIEVEQSBwcm9jZXNzIGFyZToNCg0KKiBXaGF0IHR5cGUgb2YgdmFyaWF0aW9uIGRvIGVhY2ggdmFyaWFibGUgaGF2ZT8NCg0KKiBXaGF0IGlzIHRoZSBjb3ZhcmlhdGlvbiBiZXR3ZWVuIHZhcmlhYmxlcz8NCg0KKiBBcmUgdGhlcmUgb3V0bGllcnMgcHJlc2VudCBpbiB0aGUgZGF0YT8NCg0KKiBXaGF0IHR5cGUgb2YgZGlzdHJpYnV0aW9uIGRvIHRoZSB2YXJpYWJsZXMgZm9sbG93Pw0KDQpTb21lIGV4YW1wbGVzDQoNCkZvciBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgKGZhY3RvcnMpLCB3ZSBjYW4gdXNlIGEgYmFyIGNoYXJ0Og0KYGBge3IgYmFycGxvdH0NCmdncGxvdChkYXRhID0gZGlhbW9uZHMpICsNCiAgZ2VvbV9iYXIobWFwcGluZyA9IGFlcyh4ID0gY3V0KSkgKw0KICBnZ3RpdGxlKCJDb3VudCBvZiBEaWFtb25kcyBieSBjdXQgcXVhbGl0eSIpDQpgYGANCg0KRm9yIGNvbnRpbnVvdXMgdmFyaWFibGVzLCBhIGhpc3RvZ3JhbSBjYW4gYmUgdXNlZDoNCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzKSArDQogIGdlb21faGlzdG9ncmFtKG1hcHBpbmcgPSBhZXMoeCA9IGNhcmF0KSwgYmlud2lkdGggPSAwLjUpICsNCiAgZ2d0aXRsZSgiSGlzdG9ncmFtIG9mIGNhcmF0cyIpDQpgYGANCg0KSWYgeW91IHdhbnQgdG8gYW5hbHl6ZSB0aGUgaGlzdG9ncmFtIG9mIG11bHRpcGxlIHZhcmlhYmxlcyBpbiBvbmUgc2luZ2xlIHBsb3QsIHlvdSBjYW4gdXNlIGBnZW9tX2ZyZXFwb2x5KClgIG9yIHVzZSBmYWNldHRpbmc6DQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcyAlPiUgZmlsdGVyKGNhcmF0IDwgMyksIG1hcHBpbmcgPSBhZXMoeCA9IGNhcmF0LCBjb2xvdXIgPSBjdXQpKSArDQogIGdlb21fZnJlcXBvbHkoYmlud2lkdGggPSAwLjEpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzICU+JSBmaWx0ZXIoY2FyYXQgPCAzKSwgbWFwcGluZyA9IGFlcyh4ID0gY2FyYXQsIGZpbGwgPSBjdXQpKSArDQogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4xKSArDQogIGZhY2V0X3dyYXAofiBjdXQpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KYGBgDQoNCkNob29zaW5nIHRoZSBiaW53aWR0aCBvZiB0aGUgaGlzdG9ncmFtIGNhbiB0ZWxsIGRpZmZlcmVudCBzdG9yaWVzIG9yIGNhbiByZXZlYWwgZGlmZmVyZW50IHBhdHRlcm5zOg0KDQpgYGB7ciBoaXN0IGJpbndpZHRoLCBmaWcud2lkdGg9OCxmaWcuaGVpZ2h0PTh9DQpnIDwtIGdncGxvdChkYXRhID0gZGlhbW9uZHMgJT4lIGZpbHRlcihjYXJhdCA8IDMpLCBtYXBwaW5nID0gYWVzKHggPSBjYXJhdCkpDQpnMCA8LSBnICsgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjUpICsNCiAgZ2d0aXRsZSgiQmlud2lkdGggPSAwLjUiKQ0KZzEgPC0gZyArIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4xKSArDQogIGdndGl0bGUoIkJpbndpZHRoID0gMC4xIikNCmcyIDwtIGcgKyBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuMDEpICsNCiAgZ2d0aXRsZSgiQmlud2lkdGggPSAwLjAxIikNCmczIDwtIGcwL2cxL2cyDQpnMyArIHBsb3RfYW5ub3RhdGlvbih0aXRsZSA9ICJIaXN0b2dyYW1zIHZhcnlpbmcgdGhlIGJpbndpZHRoIiwNCiAgICAgICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gIkRpZmZlcmVudCBwYXR0ZXJucyBjYW4gYXJpc2Ugd2hlbiBzZWxlY3RpbmcgZGlmZmVyZW50IGJpbndpZHRocyIpDQpgYGANCg0KQW5vdGhlciBvcHRpb24gaXMgdG8gZ28gd2l0aCBib3hwbG90czoNCg0KYGBge3IgYm94cGxvdH0NCmdncGxvdChkYXRhID0gbXBnKSArDQogIGdlb21fYm94cGxvdChtYXBwaW5nID0gYWVzKHggPSByZW9yZGVyKGNsYXNzLCBod3ksIEZVTiA9IG1lZGlhbiksIHkgPSBod3kpKSsgbGFicyh4ID0gImNsYXNzIiwgeSA9ICJod3kgbXBnIikNCmBgYA0KDQoNCkFnYWluLCB0aGVzZSBhcmUganVzdCBzb21lIGV4YW1wbGVzLCBidXQgdGhlIGxpc3Qgb2YgIGdvZXMgb24gYW5kIG9uLg0KDQoNCjxicj4NCg0KPGZvbnQgc2l6ZT0iNSI+IEdvIHRvIFtNb2RlbF0oI3dvcmtmbG93KSA8L2ZvbnQ+IA0KDQojIyMgTW9kZWwNCg0KV2hlbmV2ZXIgd2UgdHJ5IHRvIG1vZGVsIGEgdmFyaWFibGUgb3IgcGhlbm9tZW5vbSwgaXQgaXMgc2FpZCB0aGF0IHdlIGFyZSB0cnlpbmcgdG8gZ2V0IGEgc2ltcGxpZmllZCB2ZXJzaW9uIG9mIHJlYWxpdHkuIEluIGZhY3QsIGl0J3Mgc2ltcGxlciB0aGFuIHRoYXQuIFdoYXQgd2UgYXJlIHJlYWxseSB0cnlpbmcgdG8gZG8gaXMgdW5kZXJzdGFuZCB0aGUgd2F5IGEgdmFyaWFibGUgb3Igc2V0IG9mIHZhcmlhYmxlcyBjaGFuZ2UsIHdoaWxlIGlnbm9yaW5nIG9yIGVsaW1pbmF0aW5nIGV4dGVybmFsICJub2lzZSIuDQoNCkl0IGlzIHdlbGwga25vd24gdG8gbW9zdCBkYXRhIHNjaWVudGlzdHMgdGhhdCAqKnlvdSBjYW5ub3QgdXNlIHRoZSBzYW1lIGRhdGEgZm9yIG1vZGVsbGluZyBhbmQgdGVzdGluZyAob3IgZm9yZWNhc3RpbmcpKiouIFRoYXQncyB3YXkgbWFueSBwZW9wbGUgc3BsaXQgdGhlaXIgZGF0YSBpbnRvIGEgdHJhaW5pbmcgYW5kIHRlc3Rpbmcgc2V0LiBIb3dldmVyLCBhIHRydWUgZGF0YSBzY2llbnRpc3QgaGFzIHRvIGJlIGV2ZW4gc3RyaWN0ZXIgb24gaXRzIHVzZSBvZiBkYXRhOg0KDQpJdCBpcyByZWNvbW1lbmRlZCB0aGF0IHlvdSBzcGxpdCB5b3VyIGRhdGEgaW50byB0aHJlZSBzZXRzOg0KDQoxLiB+IDYwJSBvZiB5b3VyIGRhdGEgZ29lcyB0byB0aGUgKip0cmFpbmluZyoqIHNldC4gSGVyZSwgeW91IGNhbiB2aXN1YWxpemUgaXQsIHBlcmZvcm0gYWxsIHRoZSBtb2RlbCBmaXR0aW5nIGFuZCB0d2Vha2luZyB5b3Ugd2FudCwgb3ZlciBhbmQgb3ZlciBhZ2Fpbi4NCg0KMi4gfiAyMCUgb2YgeW91ciBkYXRhIHNob3VsZCBnbyB0byBhICoqcXVlcnkqKiBzZXQuIFdpdGggdGhpcyBxdWVyeSBzZXQsIHlvdSBjYW4gY29tcGFyZSBtb2RlbHMgYnkgaGFuZCBhbmQgdmlzdWFsaXplIHRoZSBvdXRjb21lcy4NCg0KMy4gVGhlIH4gMjAlICByZW1haW5pbmcgZGF0YSB3b3VsZCBjb25mb3JtIHRoZSAqKnRlc3QqKiBzZXQuIE9uY2UgeW91J3ZlIGNvbXBhcmVkIGFsbCB5b3VyIG1vZGVscyB3aXRoIHRoZSB0cmFpbmluZyBhbmQvb3IgcXVlcnkgc2V0cywgeW91IGNhbiB0ZXN0IHlvdXIgZmluYWwgbW9kZWwuIFRoaXMgdGVzdCBjYW4gb25seSBiZSBwZXJmb3JtZWQgKipPTkNFKiouIFRoaXMgZW5zdXJlcyBubyBiaWFzIGlzIGludHJvZHVjZWQgaW4gdGhlIG1vZGVsIGFuZCBpdCByZW1haW5zIGEgdHJ1ZSBmb3JlY2FzdC4NCg0KDQpXZSB3aWxsIGdvIHRocm91Z2ggbWFueSBkaWZmZXJlbnQgbW9kZWxzIGFpbWVkIHRvIHByb3ZpZGUgZm9yZWNhc3RzIGZvciBkaWZmZXJlbnQgc2l0dWF0aW9ucy4NCg0KDQoNCg0KDQo8YnI+DQoNCjxmb250IHNpemU9IjUiPiBHbyB0byBbRm9yZWNhc3RpbmddKCN3b3JrZmxvdykgPC9mb250PiANCg0KIyMgNC4gRm9yZWNhc3RpbmcgeyNmb2NzdH0NCg0KZm9yZWNhc3QNCg0KDQoNCiMNCg0KDQo8YnI+PGJyPjxicj48YnI+DQoNCioqKg0KDQojIEV4YW1wbGVzIA0KDQpgYGB7ciBkYXRhIHdvcmxkX2JhbmtfcG9wfQ0KZGF0YSgid29ybGRfYmFua19wb3AiKQ0KIyBBIHRpYmJsZTogMSwwNTYgeCAyMA0KIyAgICBjb3VudHJ5IGluZGljYXRvciBgMjAwMGAgYDIwMDFgIGAyMDAyYCBgMjAwM2AgIGAyMDA0YCAgYDIwMDVgICAgYDIwMDZgICAgYDIwMDdgDQojICAgIDxjaHI+ICAgPGNocj4gICAgICA8ZGJsPiAgPGRibD4gIDxkYmw+ICA8ZGJsPiAgIDxkYmw+ICAgPGRibD4gICAgPGRibD4gICAgPGRibD4NCiMgIDEgQUJXICAgICBTUC5VUkIuVH4gNC4yNGU0IDQuMzBlNCA0LjM3ZTQgNC40MmU0IDQuNDdlKzQgNC40OWUrNCAgNC40OWUrNCAgNC40N2UrNA0KIyAgMiBBQlcgICAgIFNQLlVSQi5HfiAxLjE4ZTAgMS40MWUwIDEuNDNlMCAxLjMxZTAgOS41MWUtMSA0LjkxZS0xIC0xLjc4ZS0yIC00LjM1ZS0xDQojICAzIEFCVyAgICAgU1AuUE9QLlR+IDkuMDllNCA5LjI5ZTQgOS41MGU0IDkuNzBlNCA5Ljg3ZSs0IDEuMDBlKzUgIDEuMDFlKzUgIDEuMDFlKzUNCiMgIDQgQUJXICAgICBTUC5QT1AuR34gMi4wNmUwIDIuMjNlMCAyLjIzZTAgMi4xMWUwIDEuNzZlKzAgMS4zMGUrMCAgNy45OGUtMSAgMy44NGUtMQ0KIyAgNSBBRkcgICAgIFNQLlVSQi5UfiA0LjQ0ZTYgNC42NWU2IDQuODllNiA1LjE2ZTYgNS40M2UrNiA1LjY5ZSs2ICA1LjkzZSs2ICA2LjE1ZSs2DQojICA2IEFGRyAgICAgU1AuVVJCLkd+IDMuOTFlMCA0LjY2ZTAgNS4xM2UwIDUuMjNlMCA1LjEyZSswIDQuNzdlKzAgIDQuMTJlKzAgIDMuNjVlKzANCiMgIDcgQUZHICAgICBTUC5QT1AuVH4gMi4wMWU3IDIuMTBlNyAyLjIwZTcgMi4zMWU3IDIuNDFlKzcgMi41MWUrNyAgMi41OWUrNyAgMi42NmUrNw0KIyAgOCBBRkcgICAgIFNQLlBPUC5HfiAzLjQ5ZTAgNC4yNWUwIDQuNzJlMCA0LjgyZTAgNC40N2UrMCAzLjg3ZSswICAzLjIzZSswICAyLjc2ZSswDQojICA5IEFHTyAgICAgU1AuVVJCLlR+IDguMjNlNiA4LjcxZTYgOS4yMmU2IDkuNzdlNiAxLjAzZSs3IDEuMDllKzcgIDEuMTVlKzcgIDEuMjFlKzcNCiMgMTAgQUdPICAgICBTUC5VUkIuR34gNS40NGUwIDUuNTllMCA1LjcwZTAgNS43NmUwIDUuNzVlKzAgNS42OWUrMCAgNC45MmUrMCAgNC44OWUrMA0KIyAuLi4gd2l0aCAxLDA0NiBtb3JlIHJvd3MsIGFuZCAxMCBtb3JlIHZhcmlhYmxlczogYDIwMDhgIDxkYmw+LCBgMjAwOWAgPGRibD4sDQojICAgYDIwMTBgIDxkYmw+LCBgMjAxMWAgPGRibD4sIGAyMDEyYCA8ZGJsPiwgYDIwMTNgIDxkYmw+LCBgMjAxNGAgPGRibD4sDQojICAgYDIwMTVgIDxkYmw+LCBgMjAxNmAgPGRibD4sIGAyMDE3YCA8ZGJsPg0KYGBgDQoNCg0KDQoNCg0KLS0tLS0tLS0tLS0tLS0tLS0NCg0KDQoqICoqUHJvZ3JhbWFjacOzbioqDQogIC0gUGFyYSBsbGV2YXIgYSBjYWJvIGl0ZXJhY2lvbmVzIGVuIG9iamV0b3MgZGUgKipSKiosIGVzIGNvbnZlbmllbnRlIHV0aWxpemFyIGZ1bmNpb25lcyBkZSBsYSBwYXF1ZXRlcsOtYSBgcHVycnJgLg0KDQoNCg==